#pragma once

#include<iostream>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/sendfile.h>
#include<sys/wait.h>

#include<algorithm>
#include<sstream>
#include<string>
#include<vector>
#include<unordered_map>
#include "httpserver.hpp"
#include "Util.hpp"
#include"Log.hpp"

#define BAD_REQUEST 400
#define OK 200
#define NOT_FOUND 404
#define SERVER_ERROR 500

#define WWW_ROOT "wwwroot"
#define HOME_PAGE "index.html"
#define HTTP_VERSION "http/1.0"
#define LINE_END "\r\n"

#define PAGA_404 "404.html"
#define PAGA_500 "500.html"
#define PAGA_400 "400.html"


static std::string& Code2Desc(int code)
{
  static std::string a;
  switch(code){
    case 200:
      a="OK";
      break;
    case 404:
      a="Not Request";
      break;
    case 400:
      a="Not found";
      break;
    case 500:
      a="Server error";
    default:
      break;
  }
  return a;
}

static std::string Suffix2Desc(std::string &_path)
{

  std::string suffix;
  size_t pos=_path.rfind(".");
  if(pos!=std::string::npos){
    suffix=_path.substr(pos);
  }
  static std::unordered_map<std::string,std::string> mp={
    {".html","text/html"},
    {".css","text/css"},
    {".is","application/javascript"},
    {".jpg","application/x-jpg"},
    {".xml","application/xml"},
  };
  auto it=mp.find(suffix);
  if(it!=mp.end()){
    return it->second;
  }
  else{
    return "text/html";
  }

}

//请求
class Request{
  public:
    //报头
    std::string request_line;
    std::vector<std::string> request_headline;
    std::string request_blank;
    std::string request_body;
    std::string meth; 
    std::string route; 
    std::string edition;
    //解析的报头
    std::unordered_map<std::string,std::string> request_kv;
    std::string request_path;
    std::string request_parameter;
    int content_length;
    //状态码
    int code;
    bool cgi;
      
  public:
    Request():cgi(false),content_length(0)
    {}
    ~Request(){}

};

//响应
class Response{
  public:
    //报头
    std::string response_line;
    std::vector<std::string> response_headline;
    std::string response_blank;
    std::string response_body;
    
    //报文属性
   // std::string response_string_content;
   // std::string response_type;
    //  std::string suffix;
    //数据大小
    int size;

  public:
    Response():size(0){}
    ~Response(){}
};

//方法
class Method{
  private:
    Request request;
    Response response;
    int sock;
    int fd;
    bool stop;
  public:
    Method(int _sock):sock(_sock),fd(-1),stop(false){}
    ~Method(){}
    bool IsStop()
    {
      return stop;
    }
  public:
    //获取并解析请求报头
    bool ObtainLine()
    {
      if(Util::ReadLine(sock,request.request_line)>0){
        Util::OneLine(request.request_line,request.meth,request.route,request.edition);
        std::transform(request.meth.begin(), request.meth.end(), request.meth.begin(), ::toupper);
        request.request_line.resize(request.request_line.size()-1);
        LOG(INFO,request.request_line);
        stop=false;
      }
      else{

        stop=true;
      }
      return this->stop;
    }
    bool ObtainHead()
    {
      std::string st;
      while(true){
        st.clear();
        if(Util::ReadLine(sock,st)<=0){
          stop=true;
          return this->stop;
        }
        if(st=="\n"){
          request.request_blank=st;
          break;
        }
        st.resize(st.size()-1);
        request.request_headline.push_back(st);
        LOG(FINO,st);
      }
      std::string k;
      std::string v;
      for(auto &e:request.request_headline){
        Util::TowLine(e,k,v,": ");
        request.request_kv.insert({k,v});
      }
      return this->stop;
    }
    bool JudgeData()
    {
      auto& a=request.meth;
      if(a=="POST"){ 
        auto e=request.request_kv.find("Content-Length");
        if(e!=request.request_kv.end()){
          LOG(FINO,"Post method,Content-Length"+e->second);
          request.content_length=atoi(e->second.c_str());
          return true;
        }
      }
      return false;
    }
    bool ObtainBody()
    {
      if(JudgeData()){
        int content_length=request.content_length;
        char ch=0;
        while(content_length){
          ssize_t s=recv(sock,&ch,1,0);
          if(s>0){
            request.request_body.push_back(ch);
            content_length--;
          }
          else{
            stop=true;
            break;
          }
        }
        LOG(FINO,request.request_body);
      }
      return this->stop;
    }
    void BuildRequest()
    {
      std::string _path;
      struct stat st;

      if(request.meth!="GET" && request.meth!="POST"){
        LOG(WARNING,"method is not right");
        request.code=BAD_REQUEST;
        goto END;
      }
      if(request.meth=="GET"){
        size_t pos=request.route.find('?');
        if(pos!=std::string::npos){
          Util::TowLine(request.route,request.request_path,request.request_parameter,"?");
          request.cgi=true;
          LOG(INFO,"GET"+request.request_path);
        }
        else{
          request.request_path=request.route;
          LOG(INFO,"GET"+request.request_path);
        }
      }
      else if(request.meth=="POST"){
        request.request_path=request.route;
        LOG(FINO,"POST "+request.request_path);
        request.cgi=true;
      }
      else{
          
      }
      _path=request.request_path;
      request.request_path=WWW_ROOT;
      request.request_path+=_path;
      if(request.request_path[request.request_path.size()-1]=='/'){
        request.request_path+=HOME_PAGE;
      }
      if(stat(request.request_path.c_str(),&st)==0){
        if(S_ISDIR(st.st_mode)){
          request.request_path+="/";
          request.request_path+=HOME_PAGE;
          stat(request.request_path.c_str(),&st); 
        } 
        if((st.st_mode&S_IXUSR) || (st.st_mode&S_IXGRP) || (st.st_mode&S_IXOTH)){
          request.cgi=true;          
        }
        response.size=st.st_size;
      }
      else{
        LOG(WARNING,request.request_path+" Not Found");
        request.code=NOT_FOUND;
        goto END;
      }

      if(request.cgi){
       request.code=ProcessCgi();    
      }
      else{
        request.code=ProcessNoncgi();     
      }

END:  
      BuliHttpResponseHelper();


      
      return;
    }
    
    void BuliHttpResponseHelper()
    {
      response.response_line=HTTP_VERSION;
      response.response_line+=" ";
      response.response_line+=std::to_string(request.code);
      response.response_line+=" ";
      response.response_line+=Code2Desc(request.code);
      response.response_line+=LINE_END;
      
      std::string _path;
      _path=WWW_ROOT;
      _path+="/";
      int code=request.code;
      switch(code){
        case OK:
          BuilOKResponse();
          break;
        case NOT_FOUND:
          _path+=PAGA_404;
          HandlError(_path);
          break;
        case SERVER_ERROR:
          _path+=PAGA_500;
          HandlError(_path);
          break;
        case BAD_REQUEST:
          _path+=PAGA_400;
          HandlError(_path);
          break;
        default:
          break;
      }
    }
    
    void BuilOKResponse()
    {
      std::string response_string_content="Content-Length: ";
      if(request.cgi){
        response_string_content+=std::to_string(response.response_body.size());
      }
      else{
        response_string_content+=std::to_string(response.size);
      }
      response_string_content+=LINE_END;
      response.response_headline.push_back(response_string_content);
      response_string_content="Contene-Type: ";
      response_string_content+=Suffix2Desc(request.request_path);
      response_string_content+=LINE_END;
      response.response_headline.push_back(response_string_content);
    }
    void HandlError(std::string paga)
    {
       request.cgi=false;
       fd=open(paga.c_str(),O_RDONLY); 
       if(fd>=0){
          struct stat st;
          stat(paga.c_str(),&st);
          response.size=st.st_size;
          std::string response_string_content="Content-Length: ";
          response_string_content+=std::to_string(st.st_size);
          response_string_content+=LINE_END;
          response.response_headline.push_back(response_string_content);

          response_string_content="Contene-Type: text/html";
          response_string_content+=LINE_END;
          response.response_headline.push_back(response_string_content);

       }

    }

    int ProcessCgi()
    {
      int code;
      int output[2];
      int input[2];
      std::string bin=request.request_path;
      std::string method_env;
      std::string parameter_env;
      std::string content_length_env;
      if(pipe(output)<0){
        LOG(ERROR,"pipe error");
        code=500;
        return code;
      }
      if(pipe(input)<0){
        LOG(ERROR,"pipe error");
        code=500;
        return code;
      }
      LOG(FINO,"pipe sucess");
      
      //以父进程为主动方
      pid_t pid=fork();
      if(pid==0){
        close(output[1]);
        close(input[0]);
        
        method_env="METHOD=";
        method_env+=request.meth;
        putenv((char*)method_env.c_str());

        if(request.meth=="GET"){
          parameter_env="PARAMETER=";
          parameter_env+=request.request_parameter;
          putenv((char*)parameter_env.c_str());
        }
        if(request.meth=="POST"){
          content_length_env="CONTENT_LENGTH=";
          content_length_env+=std::to_string(request.content_length);
          putenv((char*)content_length_env.c_str());
        }
        
        dup2(output[0],0);
        dup2(input[1],1);

        execl(bin.c_str(),bin.c_str(),nullptr);
        exit(1);
      }
      else if(pid<0){
        LOG(ERROR,"fork error");
        code=500;
        return code;
      }
      else{
        close(output[0]);
        close(input[1]);
        
        if(request.meth=="POST"){
          const char* start=request.request_body.c_str();
          int size=0;
          int total=0;  
          while((size=write(output[1],start+total,request.request_body.size()-total))>0)
            total+=size;
        }
        
        char ch=0;
        while(read(input[0],&ch,1)>0){
          response.response_body.push_back(ch);
        }
        
        int status=0;
        pid_t ret=waitpid(pid,&status,0);
        if(ret==pid){
          if(WIFEXITED(status)){
            if(WEXITSTATUS(status) == 0){
                code = OK;
            }
            else{
                code = 500;
            }
          }
          else{
            code = 500;
          }
        }
        close(output[1]);
        close(input[0]);
      }
      return code;
    }

    int ProcessNoncgi()
    {
      fd=open(request.request_path.c_str(),O_RDONLY);
      if(fd>=0){
        LOG(INFO,"open success!");
        return OK;
      }
      return 404;
    }

    //返回响应
    void RecvResponse()
    {

      send(sock,response.response_line.c_str(),response.response_line.size(),0);
      for(auto &e:response.response_headline){
        send(sock,e.c_str(),e.size(),0);
      }
      if(request.cgi){
        const char* start=response.response_body.c_str();
        size_t size=0;
        size_t total=0;
        while(total<response.response_body.size() && (size=send(sock,start+total,response.response_body.size()-total,0))>0){
            total+=size;
        }
      }
      else{
        send(sock,response.response_blank.c_str(),response.response_blank.size(),0);
        sendfile(sock,fd,nullptr,response.size);
        close(fd);
      }
    }

    void A()
    {
      if((!ObtainLine())&&(!ObtainHead())){
        ObtainBody();
      }
    }
};


//调用返回
class CallBack{

  public:
    CallBack(){}
    ~CallBack(){}

    void operator()(int sock)
    {
      HandleEnve(sock);
    }

  void HandleEnve(int sock)
  {
    LOG(INFO,"Hander Request Begin");
#ifdef DEBUG 
    char buffer[4096];
    recv(sock,buffer,sizeof(buffer),0);
    std::cout<<"---------------------begin---------"<<std::endl;
    std::cout<<buffer<<std::endl;
    std::cout<<"-----------------end------------"<<std::endl;
#else 
    Method* e=new Method(sock);
    e->A();
    if(!e->IsStop()){
      e->BuildRequest();
      e->RecvResponse();
    }
    else{
      LOG(ERROR,"Stop Buil request recv and rend");
    }
    
    delete e;
#endif 

    LOG(INFO,"Hander Request End");
  }

};
